#include "conv1.h"

using namespace std;

// prototypes
void createCPUdata(int argc, char** argv);
void initGLUT(int argc, char** argv);
void initGLEW();
void set1to1Mapping();    // sets the projection for 1:1 mapping
void initGPUTextures();   // set up texturs on gpu
void initCgProg();        // initialize the Cg shader program
void copyTexturesToGPU(); // copy CPU data to gpu texture
void printInputData();
void printResults();
void printResultstoFile(string fileName);
void render();
void readBacktoCPU(int texType);
void initFBO(void);
void usage();
void printOptionsSummary();
void printProgDesc();
void getCommandArgs(int argc, char** argv);
void attachTexturesToFBO();
bool checkFramebufferStatus();

void checkGLErrors (const char *label);

void createTextures();
void setupTexture(const GLuint texID, GLint xSize, GLint ySize);
void transferToTexture(float *data, GLuint texID, GLint xSize, GLint ySize);



/***********************************************************
 * MAIN
 ***********************************************************/
int main(int argc, char **argv)
{
    time(&rawTime);
    timeInfo = localtime(&rawTime);
    cout << asctime(timeInfo);

    if (argc != 2) usage();

    printProgDesc();

    createCPUdata(argc, argv);
    initGLUT(argc,argv);
    initGLEW();

    initFBO();
    set1to1Mapping();
    cout << "-----------------------------------------------" << endl;
    cout << "one to one mapping set ok" << flush << endl;
    initGPUTextures();
    cout << "-----------------------------------------------" << endl;
    cout << "init gpu textures ok" << flush << endl;

    //   createTextures();

    //attachTexturesToFBO();
    copyTexturesToGPU();
    cout << "-----------------------------------------------" << endl;
    cout << "copy to GPU ok" << flush << endl;


    printInputData();
    readBacktoCPU(readTex);
    printResults();


    initCgProg();
    render();
    readBacktoCPU(writeTex);
    printResults();

    if (atoi(argv[1]) == 1) {
        stringstream convFileName;
        convFileName <<  "conv-"
                << sec << min << hour
                << "-" << day << month << year;

        printResultstoFile(convFileName.str());
    }

    // clean up
    free(readTexArr);
    free(writeTexArr);
    free(convFuncTexArr);
    free(resultArr);

    glDeleteTextures(3,textureID);
    cgDestroyContext(cgContext);

    return 0;
} // main()









void getCommandArgs(int argc, char** argv)
/*********************************************************
 * DESC: Retrieves the command line arguments
 *
 *
 *********************************************************/
{
    if (argc != 1) {
        usage();
        exit(1);
    }

    //width = atoi(argv[1]);
    //height = atoi(argv[2]);

} // getCommandArgs()


void createCPUdata(int argc, char **argv)
/*********************************************************
 * DESC: create data arrays on the GPU as input for the
 *       textures on the GPU
 *********************************************************/
{
    cout << "Info: length of array allocated: "
            <<  dataHeight*dataWidth*4 << endl;

    readTexArr      = (GLfloat*)malloc(dataHeight*dataWidth*4*sizeof(GLfloat));
    writeTexArr     = (GLfloat*)malloc(dataHeight*dataWidth*4*sizeof(GLfloat));
    convFuncTexArr  = (GLfloat*)malloc(convHeight*convWidth*4*sizeof(GLfloat));
    resultArr       = (GLfloat*)malloc(dataHeight*dataWidth*4*sizeof(GLfloat));

    int index = 0;
    GLfloat colour = 0.0;
    for (int i=0;i<dataHeight; i++) {
        for (int j=0;j<dataWidth;j++) {
            index = 4*((dataWidth*i)+j);
            readTexArr[index]   = colour + 1.0;
            readTexArr[index+1] = colour + 2.0;
            readTexArr[index+2] = colour + 3.0;
            readTexArr[index+3] = colour + 4.0;
        }
    }

    index = 0;
    colour = 99.0;
    for (int i=0;i<dataHeight; i++) {
        for (int j=0;j<dataWidth;j++) {
            index = 4*((dataWidth*i)+j);
            writeTexArr[index]   = colour;
            writeTexArr[index+1] = colour;
            writeTexArr[index+2] = colour;
            writeTexArr[index+3] = colour;
        }
    }

    index = 0;
    colour = 0.5;
    for (int i=0;i<convHeight; i++) {
        for (int j=0;j<convWidth;j++) {
            index = 4*((convWidth*i)+j);
            convFuncTexArr[index]  = colour;
            convFuncTexArr[index+1] = colour + 1.0;
            convFuncTexArr[index+2] = colour + 2.0;
            convFuncTexArr[index+3] = colour + 3.0;
        }
    }

    index = 0;
    colour = -99.0;
    for (int i=0;i<dataHeight; i++) {
        for (int j=0;j<dataWidth;j++) {
            index = 4*((dataWidth*i)+j);
            resultArr[index]   = colour;
            resultArr[index+1] = colour;
            resultArr[index+2] = colour;
            resultArr[index+3] = colour;
        }
    }
    cout << "Status: CPU data created" << flush << endl;

    return;

} // createCPUdata()




void initGLUT(int argc, char** argv)
/*********************************************************
 * DESC: Initialize glut to set up a valid openGL context
 *
 *********************************************************/
{
    glutInit(&argc, argv);
    glutCreateWindow("TEST1");
}



void initGLEW()
/*********************************************************
 * DESC: Initialise the openGL extension extry points.
 *       This also checks if the initialization has suceeded
 *       and checks if the extension GLEW_ARB_fragment_program
 *       is availiable.
 *********************************************************/
{
    // init GLEW, obtain function pointers for extensions
    GLenum err = glewInit();
    // Warning this does not check if all extensions used
    // in a given implimentaion are actually supported.
    // Fuction extrey points created by glewInit() will be
    // NULL in that case!
    if (GLEW_OK != err) {
        // Problem: glewInit failed, somthing is seriously wrong.
        cerr << "Error: " << glewGetErrorString(err) << endl;
        exit (ERROR_GLEW);
    }
    // return the GLEW version string
    cout << "Info: Using GLEW " << glewGetString(GLEW_VERSION) << endl;


    // check if a particular extention is avaliable.
    // note: in getneral use GLEW_{extension_name}
    if (GLEW_ARB_fragment_program) {
        cout << "Info: Extention, GLEW_ARB_fragment_program is availiable"
                << endl;
    } else {
        cerr << "Error: Extention, GLEW_ARB_fragment_program is missing"
                << endl;
        exit (ERROR_GLEW);
    }

    return;

} // initGLEW()




void set1to1Mapping()
/*********************************************************
 * DESC: set the projection matrices such that we get
 *       1:1 mapping between pixels,texels and data when
 *       rendering.
 *
 *********************************************************/
{
    // viewport transform for 1:1 pixel=texel=data mapping
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0,dataWidth,0.0,dataHeight); // (left,right,bottom,top)
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glViewport(0,0,dataWidth,dataHeight);
    cout << "Status: Mapping set as 1:1 pixel=texel=data" << endl;

}


void initGPUTextures()
/*********************************************************
 * DESC: generate and initialise textures on the GPU
 *       This creates an unclamped (allowing numbers
 *       outside the range (0-225) floating point
 *       texture. To get fp textures it is necessary to use
 *       an offscreen buffer or a texture attached to an
 *       FBO. It is important that the correct texture target
 *       and internal texture formats are chosen
 * NOTE: This function creates two textures, one for the
 *       data and one for the convolution kernel
 *
 *********************************************************/
{
    // create textures (this creates 2 textures and stores the names as
    // textures[0] and textures[1]
    glGenTextures (3, textureID);

    // read (data) Array Texture
    // -----------------------------------------------------------
    glBindTexture(textarget,textureID[readTex]);

    // set texture parameters
    // this assumes the textures are already generated and bound
    // with glGenTextures()  and ...
    // glBindTexture()
    glTexParameteri(textarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_T, GL_CLAMP);

    // define texture with floating point format
    // note the last argument being 0 creates the texture but
    // does not copy data to it
    glTexImage2D(textarget,0,texinternalformat,dataWidth,
            dataHeight,0,GL_RGBA,GL_FLOAT,0);
    //   glEnable(GL_TEXTURE_RECTANGLE_ARB);


    if (glGetError() != GL_NO_ERROR) {
        printf("Error: glTexImage2D():\t\t\t [FAIL]\n");
        exit (ERROR_TEXTURE);
    }
    printf("Info: Created a %i by %i floating point texture.\n",
            dataWidth,dataHeight);



    // read (data) Array Texture
    // -----------------------------------------------------------
    glBindTexture(textarget,textureID[writeTex]);

    // set texture parameters
    // this assumes the textures are already generated and bound
    // with glGenTextures()  and ...
    // glBindTexture()
    glTexParameteri(textarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_T, GL_CLAMP);

    // define texture with floating point format
    // note the last argument being 0 creates the texture but
    // does not copy data to it
    glTexImage2D(textarget,0,texinternalformat,dataWidth,
            dataHeight,0,GL_RGBA,GL_FLOAT,0);
    //   glEnable(GL_TEXTURE_RECTANGLE_ARB);

    if (glGetError() != GL_NO_ERROR) {
        printf("Error: glTexImage2D():\t\t\t [FAIL]\n");
        exit (ERROR_TEXTURE);
    }
    printf("Info: Created a %i by %i floating point texture.\n",
            dataWidth,dataHeight);



    // conv kerel Array Texture
    // -----------------------------------------------------------
    glBindTexture(textarget,textureID[convFuncTex]);
    glTexParameteri(textarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexImage2D(textarget,
            0,
            texinternalformat,
            convWidth,
            convHeight,
            0,
            GL_RGBA,
            GL_FLOAT,
            0);

    // 		convFuncTexArr);
    if (glGetError() != GL_NO_ERROR) {
        printf("Error: glTexImage2D():\t\t\t [FAIL]\n");
        exit (ERROR_TEXTURE);
    }
    printf("Info: Created a %i by %i floating point texture.\n",
            convWidth,convHeight);


    return;
}




void initCgProg()
{
    // set up Cg

    // create the context - this is a container for cg programs
    cgContext = cgCreateContext();
    if (cgContext == 0) {
        cerr << "Cg Context creation failure" << endl;
        exit(-1);
    }

    cgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
    if (cgFragmentProfile == CG_PROFILE_UNKNOWN) {
        cerr << "Invalid Fragment Program profile" << endl;
        exit(-1);
    }
    cgGLSetOptimalOptions(cgFragmentProfile);

    const char* programSource = "conv.frag";
    const char* programEntry  = "convFunc";

    // create fragment program
    cgProgram  = cgCreateProgramFromFile(
            cgContext, // the contex where we want the shader added
            CG_SOURCE, // array of bytes containing the cg source
            programSource, // the source file (char *)
            cgFragmentProfile, // the profile
            programEntry,            // the entry function
            NULL);           // compiler options

    if (cgProgram == 0) {
        CGerror err = cgGetError();
        cerr << cgGetErrorString(err) << endl;
        exit(-1);
    }
    // load program
    cgGLLoadProgram(cgProgram);



}


void attachTexturesToFBO()
/*********************************************************
 * DESC: attach textures to the FBO
 *
 *********************************************************/
{
    glEnable(textarget);

    //   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
    //			     source[writeTex],
    //			     textarget,
    //			     textureID[writeTex],
    //			     0);

    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
            source[readTex],
            textarget,
            dataTexture,
            0);

    cout << endl;
    if (!checkFramebufferStatus()) {
        printf("attach FBO status: glFramebufferTexture2DEXT():\t [FAIL]\n");
        exit (ERROR_FBOTEXTURE);
    }

    return;
}






void copyTexturesToGPU()
/*********************************************************
 * DESC:
 *
 *********************************************************/
{
    //cout << source[readTex] << endl;

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
    glDrawBuffer(source[readTex]);
    glRasterPos2i(0,0);    // reset the raster postion to (top left?)
    glDrawPixels(dataWidth,    // dimentions of the pixel rectangle to be
            dataHeight,   // written to the frame buffer
            GL_RGBA,  // format of the pixel data
            GL_FLOAT, // data type for pixels
            readTexArr); // pointer to the pixel data
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);


    cout << "** " << flush << endl;

    if (!checkFramebufferStatus()) {
        printf("Copy status(read): glFramebufferTexture2DEXT():\t [FAIL]\n");
        exit (ERROR_FBOTEXTURE);
    }

    //cout << source[writeTex] << endl;
    glDrawBuffer(source[writeTex]);
    glRasterPos2i(0,0);    // reset the raster postion to (top left?)
    glDrawPixels(dataWidth,    // dimentions of the pixel rectangle to be
            dataHeight,   // written to the frame buffer
            GL_RGBA,  // format of the pixel data
            GL_FLOAT, // data type for pixels
            writeTexArr); // pointer to the pixel data

    cout << endl;
    if (!checkFramebufferStatus()) {
        printf("Copy status(write): glFramebufferTexture2DEXT():\t [FAIL]\n");
        exit (ERROR_FBOTEXTURE);
    }

    //cout << source[convFuncTex] << endl;
    //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
    //    glDrawBuffer(source[convFuncTex]);
    //    glRasterPos2i(0,0);    // reset the raster postion to (top left?)
    //    glDrawPixels(convWidth,    // dimentions of the pixel rectangle to be
    // 		convHeight,   // written to the frame buffer
    // 		GL_RGBA,  // format of the pixel data
    // 		GL_FLOAT, // data type for pixels
    //     		convFuncTexArr); // pointer to the pixel data
    //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);

    cout << endl;
    if (!checkFramebufferStatus()) {
        printf("Copy status(conv): glFramebufferTexture2DEXT():\t [FAIL]\n");
        exit (ERROR_FBOTEXTURE);
    }


    return;
} // copyTexturesToGPU()


void render()
/*********************************************************
 * DESC: render a full screen quad to render and therefore
 *       perform the fragment shader calculation
 *
 *       Cg paramters are also set up here
 *********************************************************/
{
    //   attachTexturesToFBO();

    // get parameter handles by name
    // gets the named paramter "data" from the Cg fragment shader program
    // and stores it as a CGparamter called textureHandle
    dataHandle     =  cgGetNamedParameter(cgProgram, "dataArr");
    convFuncHandle =  cgGetNamedParameter(cgProgram, "convFuncArr");
    //wParam        =  cgGetNamedParameter(cgProgram, "wTerm");
    //uMaxParam        =  cgGetNamedParameter(cgProgram, "umax");
    //vMaxParam        =  cgGetNamedParameter(cgProgram, "vmax");
    // we later use this handle to set the "data" texture to the openGL
    // texture set up in initGPUtextures() with the texture array name 'texture'

    // enable texture 'data' (read only)
    cgGLSetTextureParameter(dataHandle,dataTexture);
    cgGLEnableTextureParameter(dataHandle);
    cgGLSetTextureParameter(convFuncHandle,convTexture);
    cgGLEnableTextureParameter(convFuncHandle);

    // enable scalar 'vMax'
    //cgSetParameter1f(vMaxParam,vmax);

    // bind fragment program (in this case set up to be ident.frag)
    cgGLBindProgram(cgProgram);
    // enable fragment profile
    cgGLEnableProfile(cgFragmentProfile);

    // make quad filled to hit every pixel/texel
    glPolygonMode(GL_FRONT,GL_FILL);
    // render quad
    glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0);
    glVertex2f(0.0, 0.0);
    glTexCoord2f(dataWidth, 0.0);
    glVertex2f(dataWidth, 0.0);
    glTexCoord2f(dataWidth, dataHeight);
    glVertex2f(dataWidth, dataHeight);
    glTexCoord2f(0.0, dataHeight);
    glVertex2f(0.0, dataHeight);
    glEnd();


    cgGLDisableProfile(cgFragmentProfile);

    return;
} // render


void readBacktoCPU(int texType)
{
    // and read back
    switch (texType) {
        case writeTex:
            cout << "Status: reading back write texture" << endl;
            glReadBuffer(source[writeTex]);
            glReadPixels(0, 0, dataWidth, dataHeight,GL_RGBA,GL_FLOAT,resultArr);
            break;
        case readTex:
            cout << "Status: reading back read texture" << endl;
            glReadBuffer(source[readTex]);
            glReadPixels(0, 0, dataWidth, dataHeight,GL_RGBA,GL_FLOAT,resultArr);
            break;
        default:
            cout << "Error: attempted to read back an unknown texture" << endl;
            exit(ERROR_READBACK);
    }

    return;
}



void printInputData(){

    cout << "Data before roundtrip:" << endl;
    int index = 0;
    const int printWidth = 10;
    for (int i=0;i<dataHeight; i++) {
        for (int j=0;j<dataWidth;j++) {
            index = 4*((dataWidth*i)+j);
            cout.precision(4);
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << readTexArr[index] << " ";
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << readTexArr[index+1] << " ";
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << readTexArr[index+2] << " ";
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << readTexArr[index+3] << endl;
        }
        cout << endl;
    }

    cout << endl;



    return;
}

void printResults()
{
    cout << "Data after render: " << endl;
    //cout << "[(coord.y) (coord.x) (w) (cos(pi*w*(coord.x^2+coord.y^2))]" << endl;
    int index = 0;
    const int printWidth = 10;
    for (int i=0;i<dataHeight; i++) {
        for (int j=0;j<dataWidth;j++) {
            index = 4*((dataWidth*i)+j);
            cout.precision(4);
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << resultArr[index+1];
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << resultArr[index];
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << resultArr[index+2];;
            cout << setw(printWidth) << setiosflags(ios::left);
            cout << resultArr[index+3] << endl;
        }
        cout << endl;
    }
}





void initFBO(void)
/**********************************************************
 * DESC: Enable the openGL framebuffer extention providing
 *       full 32bit precision support and removing all
 *       unwanted clamping issues. This function creates the
 *       off-screen buffer and binds it to the framebuffer
 *       extention
 **********************************************************/
{
    cout << "Status: creating the FBO and binding it to fb" << endl;

    // Create FB0 (off-screen frame buffer)
    //   note: that binding FBO to number 0 will restore
    //   the window-system specific framebuffer at any time.
    glGenFramebuffersEXT(1, &fb); // (number of buffers to generate
    //                                       ,array array of enumerants)

    // bind offscreen buffer to fb -> this changes were we are rendering to
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);
}





void usage()
{
    cout << endl;
    cout << "USAGE: ./conv1 [OPTION]" << endl;
    cout << "   OPTION = 0: No output files" << endl;
    cout << "   OPTION = 1: Write output files" << endl;
    cout << endl;
    exit(1);
}



void printResultstoFile(string fileName) {
    /*********************************************************
     * DESC: Prints out results of GPU calcualtions to a file
     *       after the textures have been coppied back to the
     *       CPU.
     * ARGS: (string fileName) the filename of the data file
     *       to be written. In the present form this is used as
     *       the base filename
     *********************************************************/
    ofstream outFile_Re;
    ofstream outFile_Im;

    string sOutfile_Re = fileName+".Re";
    string sOutfile_Im = fileName+".Im";

    const char* cOutfile_Re = sOutfile_Re.c_str();
    const char* cOutfile_Im = sOutfile_Im.c_str();

    string messageString1 =
            "Writing w function (real part) to:  ("+sOutfile_Re+") ";
    string messageString2 =
            "Writing w function (imag part) to:  ("+sOutfile_Im+") ";

    cout << "--------------------------------------------------" << endl;
    cout << "- " << messageString1 << endl;
    cout << "- " << messageString2 << endl;
    cout << "--------------------------------------------------" << endl;

    outFile_Re.open(cOutfile_Re);
    outFile_Im.open(cOutfile_Im);

    int index=0;
    const int printWidth = 15;
    for (int i=0;i<dataHeight; i++) {
        for (int j=0;j<dataWidth;j++) {
            index = 4*((dataHeight*i)+j);
            //outFile.precision(4);

            outFile_Re << setw(printWidth) << setiosflags(ios::left);
            outFile_Re << resultArr[index+1] << "\t";
            outFile_Re << setw(printWidth) << setiosflags(ios::left);
            outFile_Re << resultArr[index] << "\t";
            outFile_Re << setw(printWidth) << setiosflags(ios::left);
            outFile_Re << resultArr[index+2] << endl;

            outFile_Im << setw(printWidth) << setiosflags(ios::left);
            outFile_Im << resultArr[index+1] << "\t";
            outFile_Im << setw(printWidth) << setiosflags(ios::left);
            outFile_Im << resultArr[index] << "\t";
            outFile_Im << setw(printWidth) << setiosflags(ios::left);
            outFile_Im << resultArr[index+3] << endl;

        }
        outFile_Re << endl;
        outFile_Im << endl;

    }

    outFile_Re.close();
    outFile_Im.close();



    return;

} // printResultsToFile()



void printOptionsSummary()
/*********************************************************
 * DESC: Prints out a summary of the command line options
 *       to the stdout
 *********************************************************/
{
    cout << endl;
    cout << "--------------------------------------------------" << endl;
    cout << "Command line options: "                             << endl;
    cout << " -Width   = " << dataWidth                          << endl;
    cout << " -Height  = " << dataHeight                         << endl;
    cout << "--------------------------------------------------" << endl;
    cout << endl;

} // printOptionsSummary()



void printProgDesc()
/*********************************************************
 * DESC: Prints a description of the program to stdout
 *
 *********************************************************/
{
    cout << "==================================================" << endl;
    cout << "     Program to perform convolution on the GPU    " << endl;
    cout << "==================================================" << endl;

    return;
}




/**
 * Checks framebuffer status.
 * Copied directly out of the spec, modified to deliver a return value.
 */
bool checkFramebufferStatus() {
    GLenum status;
    status = (GLenum) glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    switch(status) {
        case GL_FRAMEBUFFER_COMPLETE_EXT:
            return true;
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
            printf("Framebuffer incomplete, incomplete attachment\n");
            return false;
        case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
            printf("Unsupported framebuffer format\n");
            return false;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
            printf("Framebuffer incomplete, missing attachment\n");
            return false;
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
            printf("Framebuffer incomplete, attached images must have same dimensions\n");
            return false;
        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
            printf("Framebuffer incomplete, attached images must have same format\n");
            return false;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
            printf("Framebuffer incomplete, missing draw buffer\n");
            return false;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
            printf("Framebuffer incomplete, missing read buffer\n");
            return false;
    }
    return false;
}


/**
 * Checks for OpenGL errors.
 * Extremely useful debugging function: When developing,
 * make sure to call this after almost every GL call.
 */
void checkGLErrors (const char *label)
{
    GLenum errCode = glGetError();
    const GLubyte * errStr;

    if ( errCode != GL_NO_ERROR)
    {
        errStr = gluErrorString(errCode);
        printf("OpenGL ERROR: ");
        printf("%s", (char*)errStr);
        printf("(Label: ");
        printf("%s", label);
        printf(")\n.");
    }
}



void setupTexture(GLuint texID, GLint xtexSize, GLint ytexSize)
{
    // make active and bind
    glBindTexture(textarget,texID);
    // turn off filtering and wrapping modes
    glTexParameteri(textarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(textarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
    // define the texture with fp format
    glTexImage2D(textarget,         // texture target (see header file)
            0,                 // level - level n is the nth mipmap
            texinternalformat, // internal format
            xtexSize,          // width
            ytexSize,          // height
            0,                 // border
            GL_RGBA,           // format
            GL_FLOAT,          // type
            0);                // pointer to the image data in memory
    // check if that worked
    if (glGetError() != GL_NO_ERROR) {
        printf("Error: glTexImage2D():\t\t\t [FAIL]\n");
        exit (ERROR_TEXTURE);
    }
    printf("Info: Created a %i by %i floating point texture.\n",
            xtexSize,ytexSize);
    return;
}



void transferToTexture(float *data, GLuint texID, GLint xSize, GLint ySize)
{
    //   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    //		     textarget, texID, 0);
    cout << "transfer to texture..." << endl;
    glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
    glRasterPos2i(0,0);
    glDrawPixels(xSize,ySize,GL_RGBA,GL_FLOAT,data);
    //   glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
    //		     textarget, 0, 0);
    return;
}


void createTextures()
{
    cout << "Status: generating textures" << endl;
    cout << flush;
    //   glGenTextures(2,textureID);
    cout << "Status: setting up DATA texture" << endl;
    cout << flush;
    glGenTextures(1,&dataTexture);
    setupTexture(dataTexture,dataWidth,dataHeight);
    transferToTexture(readTexArr,dataTexture,dataWidth,dataHeight);
    //cout << "Status: setting up write texture" << endl;
    //setupTexture(textureID[writeTex],dataWidth,dataHeight);
    //transferToTexture(writeTexArr,textureID[writeTex],dataWidth,dataHeight);
    cout << "Status: setting up CONV texture" << endl;
    glGenTextures(1,&convTexture);
    setupTexture(convTexture,convWidth,convHeight);
    transferToTexture(convFuncTexArr,convTexture,
            convWidth,convHeight);

    // set texenv mode from modulate (the default) to replace)
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    // check if something went completely wrong
    checkGLErrors("createFBOandTextures()");
    return;
}



